export function patch(oldVnode, vnode) {
    // 判断oldVnode是一个元素节点？
    if (oldVnode.nodeType) { // 元素
        const el = createElm(vnode);
        oldVnode.appendChild(el);
    } else {
        patchVnode(oldVnode, vnode); // 从根开始比较的
    }
}
function isSameVnode(oldVnode, vnode) { // 必须标签一样key 一样才是同一个元素
    return (oldVnode.tag === vnode.tag) && (oldVnode.key === vnode.key)
}
function patchVnode(oldVnode, vnode) {
    // 比较两个节点 （节点需要能复用）
    if (!isSameVnode(oldVnode, vnode)) {
        // 如果不是相同节点，将老dom元素直接替换成新元素即可
        return oldVnode.el.parentNode.replaceChild(createElm(vnode), oldVnode.el)
    }
    // 走到这里说明之前和现在的节点是同一个节点， 要复用节点
    const el = vnode.el = oldVnode.el
    if (!oldVnode.tag) { // 文本比较文本内容，有变化复用文本节点更新内容
        if (oldVnode.text !== vnode.text) {
            el.textContent = vnode.text
        }
    }
    // 除了文本那就是元素了， 元素的话需要比较自己的属性和儿子
    updateProperties(vnode, oldVnode.data); // 更新属性，需要和老的比对
    // 比较双方儿子
    let oldChildren = oldVnode.children || [];
    let newChildren = vnode.children || [];
    // 双方都有儿子
    if (oldChildren.length > 0 && newChildren.length > 0) {
        // 比较双方的儿子
        updateChildren(el, oldChildren, newChildren); // 交给此方法来更新
    } else if (oldChildren.length > 0) {
        el.innerHTML = '';
    } else if (newChildren.length > 0) {
        for (let i = 0; i < newChildren.length; i++) {
            el.appendChild(createElm(newChildren[i]))
        }
    }
    // 之前有儿子 现在没儿子 把以前的儿子删除掉
    // 之前的没儿子 现在有儿子 直接将现在的儿子插入即可
}
// 给dom元素添加样式
function updateProperties(vnode, oldProps = {}) {
    const newProps = vnode.data || {}
    const el = vnode.el;
    // 对于属性来说新的要直接生效 但是老的里面有的新的没有还要移除
    let newStyle = newProps.style || {}
    let oldStyle = oldProps.style || {};
    for (let key in oldStyle) { // 老的样式有，新的没有要删除dom元素的样式
        if (!newStyle[key]) {
            el.style[key] = ''
        }
    }
    for (let key in oldProps) { // 老的属性有新的没有 移除这个属性
        if (!newProps[key]) {
            el.removeAttribute(key)
        }
    }
    for (let key in newProps) {
        if (key === 'style') {
            for (let styleName in newProps.style) {
                el.style[styleName] = newProps.style[styleName]
            }
        } else {
            el.setAttribute(key, newProps[key])
        }
    }
}
// 递归创建节点
function createElm(vnode) {
    let { tag, children, text } = vnode
    // 如果标签名是字符串说明是一个元素节点
    if (typeof tag === 'string') {
        // createElement DOMapi
        vnode.el = document.createElement(tag);
        updateProperties(vnode)
        children.forEach(child => vnode.el.appendChild(createElm(child)))
    } else {
        vnode.el = document.createTextNode(text)
    }
    return vnode.el
}
function updateChildren(el, oldChildren, newChildren) {
    // 对dom操作的常见优化 
    // 给你一个列表  增加一个 删除一个 倒序 反序
    // 双端比对
    let oldStartIndex = 0;
    let oldStartVnode = oldChildren[0];
    let oldEndIndex = oldChildren.length - 1;
    let oldEndVnode = oldChildren[oldEndIndex];

    let newStartIndex = 0;
    let newStartVnode = newChildren[0];
    let newEndIndex = newChildren.length - 1;
    let newEndVnode = newChildren[newEndIndex]


    function makeIndexByKey(children) {
        let map = {};

        children.forEach((child, index) => {
            map[child.key] = index; // 老的key 和索引的映射表
        })
        return map;
    }
    const map = makeIndexByKey(oldChildren)



    // 一直比较直到一方指针重合就停止
    while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
        // 如果头指针指向的结点是同一个节点，要复用这个节点
        if (!oldStartVnode) { // 比对的时候跳过空节点
            oldStartVnode = oldChildren[++oldStartIndex];
        } else if (!oldEndVnode) {
            oldEndVnode = oldChildren[--oldEndIndex];
        } else if (isSameVnode(oldStartVnode, newStartVnode)) { // 从头往后比
            patchVnode(oldStartVnode, newStartVnode)
            oldStartVnode = oldChildren[++oldStartIndex];
            newStartVnode = newChildren[++newStartIndex]
        } else if (isSameVnode(oldEndVnode, newEndVnode)) { // 从尾往前比
            patchVnode(oldEndVnode, newEndVnode)
            oldEndVnode = oldChildren[--oldEndIndex];
            newEndVnode = newChildren[--newEndIndex]
        } else if (isSameVnode(oldEndVnode, newStartVnode)) {
            // 尾部和头部比较
            patchVnode(oldEndVnode, newStartVnode); // 递归比较
            el.insertBefore(oldEndVnode.el, oldStartVnode.el); // 把尾部移动到头部
            oldEndVnode = oldChildren[--oldEndIndex]; // 老的往前移动
            newStartVnode = newChildren[++newStartIndex]; // 新的往后移动
        } else if (isSameVnode(oldStartVnode, newEndVnode)) {
            patchVnode(oldStartVnode, newEndVnode);
            el.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling); // 把尾部移动到头部
            oldStartVnode = oldChildren[++oldStartIndex];
            newEndVnode = newChildren[--newEndIndex]
        }
        // 优化diff算法， 通过dom常见操作优化出来的 
        else {
            // 用新的节点去老的里面找，如果找的到则移动复用
            // 如果找不到则创建插入，
            // 如果新的都判断完了，老的中多的就删除即可
            let moveIndex = map[newStartVnode.key]; //  用新的节点去老的里面找索引
            if (moveIndex == undefined) { // null == undefiend
                el.insertBefore(createElm(newStartVnode), oldStartVnode.el); // 老的中没有
            } else {
                let moveVnode = oldChildren[moveIndex]; // 找到要移动的节点
                el.insertBefore(moveVnode.el, oldStartVnode.el); // 将节点移动到头指针的前面
                oldChildren[moveIndex] = null;
                patchVnode(moveVnode, newStartVnode); // 比对属性和子节点
            }
            newStartVnode = newChildren[++newStartIndex]
        }
    }
    console.log(oldStartIndex,oldEndIndex)
    if (oldStartIndex <= oldEndIndex) { // 老的对于的要删除掉
        for (let i = oldStartIndex; i <= oldEndIndex; i++) {
            let child = oldChildren[i]
            if (child) {
                el.removeChild(child.el)
            }
        }
    }

    if (newStartIndex <= newEndIndex) { // 新的比老的多
        for (let i = newStartIndex; i <= newEndIndex; i++) {
            let ele = newChildren[i]
            let anchor = newChildren[newEndIndex + 1] == null ? null : newChildren[newEndIndex + 1].el
            el.insertBefore(createElm(ele), anchor);
            //  el.insertBefore(createElm(ele),null)  === el.appendChild(createElm(ele))
        }
    }

    // newStartIndex >= newEndIndex

}
// 初次渲染
// 比对的核心是从patch开始的  patch(真实的容器，虚拟节点)
//  - 根据虚拟节点创建成真实节点插入到容器中 (创建真实节点采用的是createElm) 
//  - 根据虚拟节点属性创建真实的属性updateProperties
// diff算法 
// 从patch开始的  patch(老的虚拟节点，新的虚拟节点) 
// patchVnode 比较两个节点的差异做更新的 文本、孩子、属性。。。
//  - isSameVnode 看两个节点是不是同一个节点，如果不相同删除替换即可 
//  - 复用之前的dom元素
//  - 如果是文本看文本内容是否有差异
//  - 如果是元素更新属性
//  - 如果是元素在更新儿子
//  - 更新儿子的三种情况 （updateChildren 两方都有儿子如何更新）